package com.taoding.hfhclient.netty_client;
import com.taoding.hfhclient.common.Constants;
import com.taoding.hfhclient.netty_client.service.NettyClientMessageImpl;
import com.taoding.hfhclient.netty_client.service.NettyClientMessageService;
import com.taoding.hfhclient.rabbitmq.MQConfig;
import io.netty.channel.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.concurrent.ScheduledFuture;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author liuxinghong
 * @Description: 客户端业务处理类
 * @date 2019/9/2 000216:15
 */
@Slf4j
@Component
public class ClientHandler extends SimpleChannelInboundHandler<MessageDTO> {

    @Autowired
    private NettyClientMessageService api;
    /** 发送心跳数据次数循环初始值 */
    public static int idleCount=1 ;

    /**断线重连循环次数初始值**/
    public static int reConnectCount=1;


    /**
     * 通道就绪事件
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("客户端通道id:"+ctx.channel().id().asShortText()+" 建立就绪>>>>>>>>");
        //建立连接后首次向服务端发送一条消息，用于识别客户端
        MessageDTO message = MessageDTO.builder().type(NettyConfig.NETTY_FIRST_CONNECT_TYPE)
                .messageBody("首次连接，建立客户端识别信息")
                .senderId(MQConfig.keys)
                .sendTime(DateTime.now().toString(Constants.YYYY_MM_DD_HH_MM_SS))
                .receiveIds(NettyConfig.NETTY_SERVER_ID).build();
        ctx.channel().writeAndFlush(message);
        log.info("上报客户端识别信息数据到服务端：\n"+message.toString());
        ctx.fireChannelActive();
    }


    /**
     * 接收消息事件
     * @param channelHandlerContext
     * @param message
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageDTO message) throws Exception {
        log.info("客户端: "+MQConfig.keys+"  收到服务端发来的消息:"+channelHandlerContext.channel().remoteAddress()+"    "+message.toString());
        idleCount=1; //收到消息后将心跳发送次数恢复为初始状态
        api.readMessage(message);
    }

    /**
     * 心跳请求处理
     * 每4秒发送一次心跳请求;
     *
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object obj) throws Exception {
        if (obj instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) obj;
            if (IdleState.ALL_IDLE.equals(event.state())) {  //如果读写所有的通道处于空闲状态,就发送心跳命令
                if(idleCount <= NettyConfig.NETTY_CLIENT_FCOUNT_TIMES){   //设置发送次数
                    log.info("第"+idleCount+"次向服务端发送心跳请求，时间："+new Date()+"服务端ip:"+ctx.channel().remoteAddress().toString().substring(1)+" 当前ip:"+ctx.channel().localAddress());
                    MessageDTO heartbeat = MessageDTO.builder().type(NettyConfig.NETTY_HEARTBEAT_TYPE)
                            .messageBody("第"+idleCount+"次发送心跳消息")
                            .senderId(MQConfig.keys)
                            .receiveIds(NettyConfig.NETTY_SERVER_ID)
                            .sendTime(DateTime.now().toString(Constants.YYYY_MM_DD_HH_MM_SS)).build();
                    idleCount++;
                    ChannelFuture channelFuture = ctx.channel().writeAndFlush(heartbeat);
                    if (!channelFuture.channel().isActive() || !channelFuture.channel().isWritable()){ //通道未激活 或者通道不可写
                        log.info("客户端通道存在异常，暂时不可用，即将重启客户端通道...........");
                        reBootChannel(ctx);
                    }
                }else{
                    log.info("已达到发送心跳次数上限 不再发送心跳请求,若服务端在接下来一定时间内未感知客户端,将关闭与服务器的通道.....!");
                }
            }else if (IdleState.WRITER_IDLE.equals(event.state())){//如果写的通道处于空闲状态,就发送心跳命令

            } else if (IdleState.READER_IDLE.equals(event.state())){//如果du的通道处于空闲状态,就发送心跳命令

            }
        }
    }

    /**
     * 出现异常处理办法
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.fireExceptionCaught(cause);
    }

    /**
     * 接收消息完成后触发该时间
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelReadComplete();
    }
    /**
     * 通道断开事件
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("客户端即将关闭，5秒后将进行断线重连操作........");
        ctx.fireChannelInactive();
        reBootChannel(ctx); //重启通道
        idleCount=1; //收到消息后将心跳发送次数恢复为初始状态
    }








    private void reBootChannel(ChannelHandlerContext ctx) {
        //重新连接服务器
        ScheduledFuture<?> schedule = ctx.channel().eventLoop().schedule(new Runnable() {
            @Override
            public void run() {
                log.info("正在重新连接中......");
                NettyClient.connect();
            }
        }, 5, TimeUnit.SECONDS);
    }


}
